package com.chart.demo;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
//import com.google.common.collect.Lists;
import java.util.ArrayList;

/**
 *  * Pie chart with multiple items.  
 */
public class PieChartView extends View {
	public static final String TAG = "PieChartView";
	public static final boolean LOGD = false;
	private static final boolean FILL_GRADIENT = false;
	private ArrayList<Slice> mSlices = new ArrayList<PieChartView.Slice>();// Lists.newArrayList();
	private int mOriginAngle;
	private Matrix mMatrix = new Matrix();
	private Paint mPaintOutline = new Paint();
	private Path mPathSide = new Path();
	private Path mPathSideOutline = new Path();
	private Path mPathOutline = new Path();
	private int mSideWidth;

	public class Slice {
		public long value;
		public Path path = new Path();
		public Path pathSide = new Path();
		public Path pathOutline = new Path();
		public Paint paint;

		public Slice(long value, int color) {
			this.value = value;
			this.paint = buildFillPaint(color, getResources());
		}
	}

	public PieChartView(Context context) {
		this(context, null);
	}

	public PieChartView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public PieChartView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mPaintOutline.setColor(Color.BLACK);
		mPaintOutline.setStyle(Style.STROKE);
		mPaintOutline.setStrokeWidth(3f * getResources().getDisplayMetrics().density);
		mPaintOutline.setAntiAlias(true);
		mSideWidth = (int) (20 * getResources().getDisplayMetrics().density);
		setWillNotDraw(false);
	}

	private static Paint buildFillPaint(int color, Resources res) {
		final Paint paint = new Paint();
		paint.setColor(color);
		paint.setStyle(Style.FILL_AND_STROKE);
		paint.setAntiAlias(true);
		if (FILL_GRADIENT) {
			final int width = (int) (280 * res.getDisplayMetrics().density);
			paint.setShader(new RadialGradient(0, 0, width, color, darken(color), TileMode.MIRROR));
		}
		return paint;
	}

	public void setOriginAngle(int originAngle) {
		mOriginAngle = originAngle;
	}

	public void addSlice(long value, int color) {
		mSlices.add(new Slice(value, color));
	}

	public void removeAllSlices() {
		mSlices.clear();
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		final float centerX = getWidth() / 2;
		final float centerY = getHeight() / 2;
		mMatrix.reset();
		mMatrix.postScale(0.665f, 0.95f, centerX, centerY);
		mMatrix.postRotate(-40, centerX, centerY);
		generatePath();
	}

	public void generatePath() {
		if (LOGD)
			Log.d(TAG, "generatePath()");
		long total = 0;
		for (Slice slice : mSlices) {
			slice.path.reset();
			slice.pathSide.reset();
			slice.pathOutline.reset();
			total += slice.value;
		}

		mPathSide.reset();
		mPathSideOutline.reset();
		mPathOutline.reset();
		// bail when not enough stats to render
		if (total == 0) {
			invalidate();
			return;

		}
		final int width = getWidth();
		final int height = getHeight();

		final RectF rect = new RectF(0, 0, width, height);
		final RectF rectSide = new RectF();
		rectSide.set(rect);
		rectSide.offset(-mSideWidth, 0);

		mPathSide.addOval(rectSide, Direction.CW);
		mPathSideOutline.addOval(rectSide, Direction.CW);
		mPathOutline.addOval(rect, Direction.CW);

		int startAngle = mOriginAngle;
		for (Slice slice : mSlices) {

			final int sweepAngle = (int) (slice.value * 360 / total);
			final int endAngle = startAngle + sweepAngle;

			final float startAngleMod = startAngle % 360;
			final float endAngleMod = endAngle % 360;
			final boolean startSideVisible = startAngleMod > 90 && startAngleMod < 270;
			final boolean endSideVisible = endAngleMod > 90 && endAngleMod < 270;

			// draw slice
			slice.path.moveTo(rect.centerX(), rect.centerY());
			slice.path.arcTo(rect, startAngle, sweepAngle);
			slice.path.lineTo(rect.centerX(), rect.centerY());

			if (startSideVisible || endSideVisible) {

				// when start is beyond horizon, push until visible
				final float startAngleSide = startSideVisible ? startAngle : 450;
				final float endAngleSide = endSideVisible ? endAngle : 270;
				final float sweepAngleSide = endAngleSide - startAngleSide;

				// draw slice side
				slice.pathSide.moveTo(rect.centerX(), rect.centerY());
				slice.pathSide.arcTo(rect, startAngleSide, 0);
				slice.pathSide.rLineTo(-mSideWidth, 0);
				slice.pathSide.arcTo(rectSide, startAngleSide, sweepAngleSide);
				slice.pathSide.rLineTo(mSideWidth, 0);
				slice.pathSide.arcTo(rect, endAngleSide, -sweepAngleSide);

			}

			// draw slice outline
			slice.pathOutline.moveTo(rect.centerX(), rect.centerY());
			slice.pathOutline.arcTo(rect, startAngle, 0);
			if (startSideVisible) {

				slice.pathOutline.rLineTo(-mSideWidth, 0);

			}
			slice.pathOutline.moveTo(rect.centerX(), rect.centerY());
			slice.pathOutline.arcTo(rect, startAngle + sweepAngle, 0);
			if (endSideVisible) {

				slice.pathOutline.rLineTo(-mSideWidth, 0);

			}

			startAngle += sweepAngle;

		}

		invalidate();

	}

	@Override
	protected void onDraw(Canvas canvas) {

		canvas.concat(mMatrix);

		for (Slice slice : mSlices) {

			canvas.drawPath(slice.pathSide, slice.paint);

		}
		canvas.drawPath(mPathSideOutline, mPaintOutline);

		for (Slice slice : mSlices) {

			canvas.drawPath(slice.path, slice.paint);
			canvas.drawPath(slice.pathOutline, mPaintOutline);

		}
		canvas.drawPath(mPathOutline, mPaintOutline);

	}

	public static int darken(int color) {

		float[] hsv = new float[3];
		Color.colorToHSV(color, hsv);
		hsv[2] /= 2;
		hsv[1] /= 2;
		return Color.HSVToColor(hsv);

	}
}